#include "MCCircleVisual.h"
#include "MCCircle2D.h"
#include "MCPoint2D.h"
#include <MCGripPoint.h>
#include <MCLineSegment2D.h>
#include <MCSettings.h>

MCCircleVisual::MCCircleVisual(ICanvas *presenter, const MCCircle2D &circle) :
    MCGraphicsItem(presenter),
    _circleModel(circle)
{
    initGripPoints();
}

void MCCircleVisual::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    setPainter(painter);

    drawCircle(transformed().center(), transformed().radius());
}

bool MCCircleVisual::contains(const QPointF &point) const
{
    MCCircle2D model = _circleModel;
    MCPoint2D center = model.center();
    double d = std::sqrt(std::pow(point.x() - center.x, 2) + std::pow(point.y() - center.y, 2));

    return d < model.radius();
}

QPainterPath MCCircleVisual::shape() const
{
    QPainterPath path;

    MCPoint2D center = transformed().center();
    double radius = transformed().radius();
    auto draggingZone = MCSettings::CircleClickableZone;

    path.addEllipse(QPointF(center.x, center.y), draggingZone, draggingZone);
    path.addEllipse(QPointF(center.x, center.y), radius + draggingZone, radius + draggingZone);
    path.addEllipse(QPointF(center.x, center.y), radius - draggingZone, radius - draggingZone);

    return path;
}

MCCircle2D MCCircleVisual::circle() const
{
    return _circleModel;
}

void MCCircleVisual::setGripAt(IGripPoint *grip, const MCPoint2D &pos)
{
    if (grip->index() == 0)
    {
        _circleModel.move(pos - _circleModel.center());
    }
    else if (grip->index() > 0 && grip->index() <= 4)
    {
        _circleModel.update(_circleModel.center(), MCLineSegment2D(_circleModel.center(), pos).length());
    }

    resetGrips();
}

void MCCircleVisual::resetGrips()
{
    gripPoints[0]->update(_circleModel.center());
    gripPoints[1]->update(_circleModel.center().x + _circleModel.radius(), _circleModel.center().y);
    gripPoints[2]->update(_circleModel.center().x - _circleModel.radius(), _circleModel.center().y);
    gripPoints[3]->update(_circleModel.center().x, _circleModel.center().y + _circleModel.radius());
    gripPoints[4]->update(_circleModel.center().x, _circleModel.center().y - _circleModel.radius());
}

MCCircle2D MCCircleVisual::transformed() const
{
    MCPoint2D center = presenter->screenToWorld(_circleModel.center());
    double radius = _circleModel.radius() / presenter->scale();

    return MCCircle2D(center, radius);
}

void MCCircleVisual::initGripPoints()
{
    gripPoints.push_back(new MCGripPoint(this, CenterPoint, 0, _circleModel.center()));
    gripPoints.push_back(new MCGripPoint(this, RadiusPoint, 1, MCPoint2D(_circleModel.center().x + _circleModel.radius(), _circleModel.center().y)));
    gripPoints.push_back(new MCGripPoint(this, RadiusPoint, 2, MCPoint2D(_circleModel.center().x - _circleModel.radius(), _circleModel.center().y)));
    gripPoints.push_back(new MCGripPoint(this, RadiusPoint, 3, MCPoint2D(_circleModel.center().x, _circleModel.center().y + _circleModel.radius())));
    gripPoints.push_back(new MCGripPoint(this, RadiusPoint, 4, MCPoint2D(_circleModel.center().x, _circleModel.center().y - _circleModel.radius())));
}

void MCCircleVisual::update(const MCPoint2D &center, double radius)
{
    _circleModel = MCCircle2D(center, radius);
}

void MCCircleVisual::update(const MCCircle2D &circle)
{
    _circleModel = circle;
}
